package cn.slg.module.system.service.pk;

import cn.slg.module.system.dal.dataobject.state.PersistenceState;
import cn.slg.module.system.dal.dataobject.state.PropertyState;
import cn.slg.module.system.service.pk.bo.PKBO;
import cn.slg.module.system.dal.dataobject.group.Group;
import cn.slg.module.system.dal.dataobject.wujiang.WuJiangDO;
import cn.slg.module.system.service.pk.skill.AllSkill;
import cn.slg.module.system.service.pk.skill.SKillCount;
import cn.slg.module.system.service.pk.utils.ReflectUtils;
import cn.slg.module.system.service.pk.utils.Round;
import cn.slg.module.system.service.wujiang.WuJiangService;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

import static cn.slg.module.system.service.pk.utils.Die.whetherContinue;
import static cn.slg.module.system.service.pk.utils.DieAndRescue.die;
import static cn.slg.module.system.service.pk.utils.Print.*;
import static cn.slg.module.system.service.pk.utils.Show.*;

@Service("pKService")
public class PKServiceImpl implements PKService {


    @Resource
    private WuJiangService wuJiangService;

    @Override
    public void pk(PKBO PKBO) {
        // 拿到 PKBO 的两只战斗队伍
        Group firstGroup = PKBO.getFirstGroup();
        Group secondGroup = PKBO.getSecondGroup();

        // 拿到两只战斗队伍的所有武将
        List<WuJiangDO> firstGroupAll = firstGroup.getAllGroup();
        List<WuJiangDO> secondGroupAll = secondGroup.getAllGroup();

        // 搜集两个List的所有武将的id，
        List<Integer> allIds = firstGroupAll.stream().map(WuJiangDO::getId).collect(Collectors.toList());
        allIds.addAll(secondGroupAll.stream().map(WuJiangDO::getId).toList());

        // 基于武将id集合，一次查询数据库
        List<WuJiangDO> wuJiangByIds = wuJiangService.getWuJiangByIds(allIds);

        // 建立一个map, 武将id : 武将
        Map<Integer, WuJiangDO> wuJiangMapById = wuJiangByIds.stream().collect(Collectors.toMap(WuJiangDO::getId, wuJiangDO -> wuJiangDO));

        // 处理这些武将之间的关系
        init(firstGroup, secondGroup, wuJiangMapById);

        // 处理武将战法的发动顺序
        initSkill(PKBO);

        // 开始战斗
        battle(PKBO);

        // TODO 战斗结束后的结算部分在这写
        countShow(firstGroup, secondGroup);

    }

    // 初始化时，处理武将战法的发动顺序
    private void initSkill(PKBO PKBO) {


        // 拿到两队所有武将，为他们初始化各自技能发动顺序
        List<WuJiangDO> firstGroupList = PKBO.getFirstGroup().getAllGroup();
        List<WuJiangDO> secondGroupList = PKBO.getSecondGroup().getAllGroup();
        initSkill(firstGroupList);
        initSkill(secondGroupList);
    }

    private void initSkill(List<WuJiangDO> groupList) {
        groupList.forEach(wuJiangDO -> {
            // 将该武将所有技能按照先后顺序排列形成List
            List<String> allSkill = new ArrayList<>() {
                {
                    add(wuJiangDO.getFirstSkill());
                    add(wuJiangDO.getSecondSkill());
                    add(wuJiangDO.getThirdSkill());
                }
            };
            // 这里必须使用LinkedHashMap,确保依照put()顺序排序,即按照 第一二三技能排序
            Map<String, Integer> prepareSkillOrderMap = new LinkedHashMap<>();
            Map<String, Integer> fightSkillOrderMap = new LinkedHashMap<>();

            // 如果有同名技能,这里put时会重置,所以武将携带相同技能,只能生效前一个.
            allSkill.forEach(skill -> {
                if (skill.length() > 0) {
                    // 这里初始化技能的统计
                    wuJiangDO.getSkillCount().put(skill, new SKillCount(skill));

                    switch (skill.substring(0, skill.indexOf("_"))) {
                        case "被动":
                            prepareSkillOrderMap.put(skill, 0);
                            break;
                        case "阵法":
                            prepareSkillOrderMap.put(skill, 1);
                            break;
                        case "兵种":
                            prepareSkillOrderMap.put(skill, 2);
                            break;
                        case "指挥":
                            prepareSkillOrderMap.put(skill, 3);
                            break;
                        case "内政":
                            prepareSkillOrderMap.put(skill, 4);
                            break;
                        case "主动":
                            fightSkillOrderMap.put(skill, 5);
                            break;
                        case "突击":
                            fightSkillOrderMap.put(skill, 7);
                            break;
                    }
                }
            });

            // 再对LinkedHashMap按照value排序,即按照 技能类型排序
            LinkedHashMap<String, Integer> prepareSkillOrderMapSorted = prepareSkillOrderMap.entrySet().stream()
                    .sorted(Map.Entry.<String, Integer>comparingByValue())
                    .collect(Collectors.toMap(
                            Map.Entry::getKey,
                            Map.Entry::getValue,
                            (oldVal, newVal) -> oldVal,
                            LinkedHashMap::new
                    ));
            LinkedHashMap<String, Integer> fightSkillOrderMapSorted = fightSkillOrderMap.entrySet().stream()
                    .sorted(Map.Entry.<String, Integer>comparingByValue())
                    .collect(Collectors.toMap(
                            Map.Entry::getKey,
                            Map.Entry::getValue,
                            (oldVal, newVal) -> oldVal,
                            LinkedHashMap::new
                    ));

            // 排好序后,获取技能名,封装为List
            wuJiangDO.setPrepareSkillOrder(prepareSkillOrderMapSorted.keySet().stream().toList());
            wuJiangDO.setFightSkillOrder(fightSkillOrderMapSorted.keySet().stream().toList());
        });
    }


    private static void battle(PKBO PKBO) {
        // 准备回合
        prepare(PKBO);

        // 循环八回合
        eightCircle(PKBO);


    }

    // 存放第几回合
    private static final Map<Integer, String> round = new HashMap<>();

    static {
        round.put(1, "一");
        round.put(2, "二");
        round.put(3, "三");
        round.put(4, "四");
        round.put(5, "五");
        round.put(6, "六");
        round.put(7, "七");
        round.put(8, "八");
    }

    // 循环八回合
    private static void eightCircle(PKBO PKBO) {
        Group firstGroup = PKBO.getFirstGroup();
        Group secondGroup = PKBO.getSecondGroup();

        // 拿到所有武将
        List<WuJiangDO> firstGroupAll = firstGroup.getAllGroup();
        List<WuJiangDO> secondGroupAll = secondGroup.getAllGroup();
        ArrayList<WuJiangDO> allWuJiang = new ArrayList<>();
        allWuJiang.addAll(firstGroupAll);
        allWuJiang.addAll(secondGroupAll);

        // 各自战死的武将的List
        List<WuJiangDO> firstGroupDies = firstGroup.getDies();
        List<WuJiangDO> secondGroupDies = secondGroup.getDies();

        // 各自能战斗的武将的List
        List<WuJiangDO> firstGroupFight = firstGroup.getFightGroup();
        List<WuJiangDO> secondGroupFight = secondGroup.getFightGroup();

        // 存放所有能参与战斗的武将,用于每回合初基于速度确定先后顺序
        List<WuJiangDO> canFightWuJiangs = new ArrayList<>();
        canFightWuJiangs.addAll(firstGroupFight);
        canFightWuJiangs.addAll(secondGroupFight);

        // 是否继续回合循环
        boolean goon = true;

        // 里面放着回合数，为带回合效果的技能提供支持
        Round roundNumber = new Round((byte) 1);

        // 8回合循环
        for (int i = roundNumber.getNumber(); i <= 8; i++) {
            formatStringPrint("[ 第" + round.get(i) + "回合 ]\n", 33, 0);

            // 本回合开始，执行一些回合效果，比如：
            // TODO 回合开始时的治疗，放在这里

            // 所有武将（包括战死的）伤兵的10%变死兵。
            allWuJiang.stream().forEach(wuJiangDO -> {
                die(wuJiangDO, false, false);
            });

            // 先基于速度进行排序
            canFightWuJiangs = canFightWuJiangs.stream().sorted(Comparator.comparing(WuJiangDO::getPvSuDu).reversed()).collect(Collectors.toList());

            // TODO 武将如果有先攻，将其提前。 1.先反着遍历
            // TODO 武将如果有遇袭，将其滞后。 2.再正着遍历
            // TODO 武将如果同时有先攻和遇袭，相互抵消，不用动。即以上两个遍历时，要判断对象是否同时有两种状态。


            // 本回合内每个武将依次行动
            // 如果在循环期间,有武将死了,就加入到各自的Group的dies,循环结束后,统一从 allFightWuJiang remove
            for (WuJiangDO fightWuJiang : canFightWuJiangs) {
                // 这里判定战斗是否结束,结束了,就没必要继续走下去了
                if (!whetherContinue(PKBO)) {
                    break;
                }

                // 循环到相应的武将,要先判断他是否已经死了,死了就跳过,没死就继续
                if ((firstGroupDies.contains(fightWuJiang)) || (secondGroupDies.contains(fightWuJiang))) {
                    continue;
                }

                // 该武将战斗回合
                fight(fightWuJiang, roundNumber.getNumber());
            }
            // 本回合结束，执行一些回合效果，比如：


            // 本回合结束,将战死的武将从 allFightWuJiang remove,避免下回合轮到他
            canFightWuJiangs.removeAll(firstGroupDies);
            canFightWuJiangs.removeAll(secondGroupDies);

            // 判断一下，是否有必要继续下一回合
            if (!whetherContinue(PKBO)) {
                break;
            }

            // TODO 遍历这些能战斗的武将 allFightWuJiang，处理他们的状态，
            // 每回合结束，先将状态持续回合数 -1 ，再看状态是否要继续存续，
            // 状态不能存续了，就移除他们。
            removePropertyState(allWuJiang);

            // TODO 持续性状态存续了，就移除他们
            removePersistenceState(allWuJiang);

            // 将回合数+1
            roundNumber.setNumber((byte) (roundNumber.getNumber() + 1));
        }

        // 战斗结束，伤兵变死兵机制
        System.out.println("战斗结束，伤兵变死兵机制********");
        allWuJiang.stream().forEach(wuJiangDO -> {
            die(wuJiangDO, true, wuJiangDO.getTeammates().getMain().getIsDie());
        });

    }

    // 移除到期的持续性状态
    private static void removePersistenceState(List<WuJiangDO> canFightWuJiangs) {
        canFightWuJiangs.forEach(canFightWuJiang -> {
            List<PersistenceState> persistenceStates = canFightWuJiang.getPersistenceStates();
            for (int i = 0; i < persistenceStates.size(); i++) {
                PersistenceState persistenceState = persistenceStates.get(i);
                persistenceState.setRemainTimes((byte) (persistenceState.getRemainTimes() - 1));
                if (persistenceState.getRemainTimes() == 0) {
                    // 移除该持续性状态
                    persistenceStates.remove(persistenceState);

                    // 显示移除提示信息
                    // 【张飞】来自【施加者】的【受到伤害降低】效果被移除
                    WuJiangDO inflictor = persistenceState.getInflictor();
                    WuJiangDO receiver = persistenceState.getReceiver();
                    System.out.println("本回合结束，移除持续性状态****");
                    System.out.println(spacing1() + showName(receiver) + "来自" + showName(inflictor) +
                            "的" + parcel1(persistenceState.getStateName()) + "效果被移除");

                    // 索引前移一位
                    i--;
                }
            }
        });
    }

    // 移除到期的属性状态
    private static void removePropertyState(List<WuJiangDO> canFightWuJiangs) {
        System.out.println("本回合结束，移除属性状态****");

        canFightWuJiangs.forEach(canFightWuJiang -> {
            List<PropertyState> propertyStates = canFightWuJiang.getPropertyStates();
            for (int i = 0; i < propertyStates.size(); i++) {
                PropertyState propertyState = propertyStates.get(i);
                propertyState.setRemainTimes((byte) (propertyState.getRemainTimes() - 1));
                if ((propertyState.getRemainTimes()) == 0) {

                    // 调用移除时的函数
                    propertyState.end();

                    // 显示移除提示信息
                    // 【张飞】来自【施加者】的【受到伤害降低】效果被移除
                    WuJiangDO inflictor = propertyState.getInflictor();
                    WuJiangDO receiver = propertyState.getReceiver();

                    System.out.println(spacing1() + showName(receiver) + "来自" + showName(inflictor) +
                            "的" + parcel1(propertyState.getStateName()) + "效果被移除");

                    // 索引前移一位
                    i--;
                }
            }
        });
    }

    /**
     * @param wuJiangDO   轮到这个能战斗的武将
     * @param roundNumber 第几回合
     */
    private static void fight(WuJiangDO wuJiangDO, byte roundNumber) {

        formatStringPrint(showName(wuJiangDO), 29, 0);
        formatStringPrint(" 行动回合\n", 33, 0);

        formatStringPrint(spacing1() + showBingAndName(wuJiangDO) + "开始行动\n", 29, 0);

        // 1.首先触发各种持续性状态(除了急救)，并发动自己的指挥战法
        boolean canAction = wuJiangDO.whenAction(roundNumber);

        if (canAction) {
            // 2.发动主动战法,
            if (wuJiangDO.canSkillAttack()) {
                // 拿到所有可以发动的战法
                List<String> fightSkillOrder = wuJiangDO.getFightSkillOrder();

                // 依次发动所有主动战法
                for (String skillName : fightSkillOrder) {
                    // 只有主动技能，才能发动
                    if (skillName.contains("主动_")) {
                        // 自己没死，才能继续发动下一个战法
                        if (!wuJiangDO.getIsDie()) {
                            // 战斗能够继续，才能继续发动下一个战法
                            if (!(wuJiangDO.getTeammates().getMain().getIsDie()) && !(wuJiangDO.getEnemy().getMain().getIsDie())) {
                                // TODO 自己每次发动战法前，不能处于如 震慑、计穷、捕获这三种状态。
                                if (wuJiangDO.canAction() && wuJiangDO.canSkillAttack()) {
                                    activeSkill(wuJiangDO, skillName, roundNumber);
                                } else {
                                    // TODO 如果本回合不能发动主动战法，记得把该武将要在本回合发动的主动战法清掉
                                    wuJiangDO.getPreparingSkill().clear();
                                }
                            }
                        }
                    }
                }
            }

            if (!(wuJiangDO.getTeammates().getMain().getIsDie()) && !(wuJiangDO.getEnemy().getMain().getIsDie())) {
                // 只有双方主将都没死,才能继续普攻
                // 3.进行普攻
                if (wuJiangDO.canNormalAttack()) {
                    // TODO 是否处于连击状态，可以普工两次
                    if (false) {
                        boolean isEnd = wuJiangDO.normalAttack();
                        if (!isEnd) {
                            wuJiangDO.normalAttack();
                        }
                    } else {
                        wuJiangDO.normalAttack();
                    }
                }
            }
        }
    }

    // TODO 应该有个isEnd返回值，确定是否战斗继续
    public static void activeSkill(WuJiangDO wuJiangDO, String skillName, byte roundNumber) {

        String skillFuncName = AllSkill.skillNameToFuncName.get(skillName);
        if (Objects.nonNull(skillFuncName)) {  // 如果技能名有对应的函数名
            // 拿到函数
            Method method = AllSkill.allSkillFunc.get(skillFuncName);
            if (method != null) {       // 如果函数名有对应的静态函数对象
                try {
                    // 先反射，基于该技能名，获取该技能的level
                    // 先基于技能名，确定是该武将的第几个技能。
                    String whichSkill = ReflectUtils.findPropertyNameByValue(wuJiangDO, skillName);
                    // 再确定该武将的该技能的等级
                    Byte skillLevel = (Byte) (ReflectUtils.findPropertyValueByName(wuJiangDO, whichSkill + "Level"));
                    //
                    String skillType = skillName.substring(0, skillName.indexOf("_"));
                    switch (skillType) {
                        case "主动":
                            // 反射调用 主动战法
                            method.invoke(null, method, wuJiangDO, skillName, skillLevel, roundNumber);
                            break;
                        case "指挥":
                            // 反射调用 指挥战法
                            method.invoke(null, method, wuJiangDO, skillName, skillLevel, roundNumber);
                            break;
                    }
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }

        }


    }


    // 准备回合
    private static void prepare(PKBO PKBO) {
        formatStringPrint("[ 准备回合 ]\n", 33, 0);

        // 部队士气加成，基于部队士气，设置 harmRateByShiQi
        prepareShiQi(PKBO);

        // 部队属性初始化，并基于兵种适性加成调节属性
        prepareBingZhong(PKBO);

        // TODO 城池建筑加成

        // TODO 战法加成
        // 执行准备回合可以发动的战法：被动、阵法、兵种、指挥战法
        //
        prepareSkill(PKBO);

    }

    private static void prepareSkill(PKBO PKBO) {
        formatStringPrint(" 准备战法\n", 33, 0);
        // 此时武将并不依照速度高低执行，而是按照队伍先后执行。
        // 即一个队伍的每个武将依次发动上面四种技能，另一个队伍继续。
        List<WuJiangDO> firstGroup = PKBO.getFirstGroup().getFightGroup();
        List<WuJiangDO> secondGroup = PKBO.getSecondGroup().getFightGroup();
        prepareSkill(firstGroup);
        prepareSkill(secondGroup);
    }

    private static void prepareSkill(List<WuJiangDO> group) {
        group.forEach(wuJiangDO -> {
            List<String> prepareSkillOrder = wuJiangDO.getPrepareSkillOrder();
            prepareSkillOrder.forEach(skillName -> {
                // TODO 准备回合发动战法
                String substring = skillName.substring(0, skillName.indexOf("_"));
                // 被动 阵法 兵种 指挥
                switch (substring) {
                    case "被动":
                        break;
                    case "阵法":
                        break;
                    case "兵种":
                        break;
                    case "指挥":
                        activeSkill(wuJiangDO, skillName, (byte) 0);
                        break;
                }
            });
        });
    }

    // 准备回合，基于队伍兵种，调节两只队伍武将的属性，也在这里初始化各武将的总属性
    private static void prepareBingZhong(PKBO PKBO) {
        formatStringPrint(" 兵种适性加成\n", 33, 0);

        Group firstGroup = PKBO.getFirstGroup();
        Group secondGroup = PKBO.getSecondGroup();

        //
        prepareBingZhong(firstGroup);
        prepareBingZhong(secondGroup);

    }

    private static void logBingZhong(Integer userId, String name, String bingZhong, String bingZhongShiXing, Float propertyRateByBingZhong) {
        String str1 = "   " + bingZhong + "  [" + userId.toString() + "+" + name + "]"
                + "的[" + bingZhong + "]"
                + "兵种适性为" + "[" + bingZhongShiXing + "]，" +
                "属性调整为原来的 " + percentInstance.format(propertyRateByBingZhong) + "\n";

        formatStringPrint(str1, 29, 0);
    }

    // 初始化武将总属性值，并基于兵种适性，调节武将的属性
    private static void prepareBingZhong(Group group) {
        String bingZhong = group.getBingZhong();

        List<WuJiangDO> groupList = group.getFightGroup();

        groupList.forEach(wuJiangDO -> {
            // 基于队伍的兵种，设置武将的兵种
            wuJiangDO.setBingZhong(bingZhong);

            Float propertyRateByBingZhong = 0f;
            String bingZhongShiXing = " ";
            switch (bingZhong) {
                case "骑兵":
                    String qiShiXing = wuJiangDO.getQiShiXing();
                    bingZhongShiXing = qiShiXing;
                    switch (qiShiXing) {
                        case "S":
                            propertyRateByBingZhong = 1.2f;
                            break;
                        case "A":
                            propertyRateByBingZhong = 1f;
                            break;
                        case "B":
                            propertyRateByBingZhong = 0.85f;
                            break;
                        case "C":
                            propertyRateByBingZhong = 0.7f;
                            break;
                    }
                    break;
                case "盾兵":
                    String dunShiXing = wuJiangDO.getDunShiXing();
                    bingZhongShiXing = dunShiXing;
                    switch (dunShiXing) {
                        case "S":
                            propertyRateByBingZhong = 1.2f;
                            break;
                        case "A":
                            propertyRateByBingZhong = 1f;
                            break;
                        case "B":
                            propertyRateByBingZhong = 0.85f;
                            break;
                        case "C":
                            propertyRateByBingZhong = 0.7f;
                            break;
                    }
                    break;
                case "弓兵":
                    String gongShiXing = wuJiangDO.getGongShiXing();
                    bingZhongShiXing = gongShiXing;
                    switch (gongShiXing) {
                        case "S":
                            propertyRateByBingZhong = 1.2f;
                            break;
                        case "A":
                            propertyRateByBingZhong = 1f;
                            break;
                        case "B":
                            propertyRateByBingZhong = 0.85f;
                            break;
                        case "C":
                            propertyRateByBingZhong = 0.7f;
                            break;
                    }
                    break;
                case "枪兵":
                    String qiangShiXing = wuJiangDO.getQiangShiXing();
                    bingZhongShiXing = qiangShiXing;
                    switch (qiangShiXing) {
                        case "S":
                            propertyRateByBingZhong = 1.2f;
                            break;
                        case "A":
                            propertyRateByBingZhong = 1f;
                            break;
                        case "B":
                            propertyRateByBingZhong = 0.85f;
                            break;
                        case "C":
                            propertyRateByBingZhong = 0.7f;
                            break;
                    }
                    break;
                case "器械":
                    String qiXieShiXing = wuJiangDO.getQiXieShiXing();
                    bingZhongShiXing = qiXieShiXing;
                    switch (qiXieShiXing) {
                        case "S":
                            propertyRateByBingZhong = 1.2f;
                            break;
                        case "A":
                            propertyRateByBingZhong = 1f;
                            break;
                        case "B":
                            propertyRateByBingZhong = 0.85f;
                            break;
                        case "C":
                            propertyRateByBingZhong = 0.7f;
                            break;
                    }
                    break;
            }
            Byte level = wuJiangDO.getLevel();
            // 武将总属性值 = 基础属性+（等级+属性加点）*属性倍率
            wuJiangDO.setPvWuLi(wuJiangDO.getPbWuLi() + (level + wuJiangDO.getPaWuLi()) * wuJiangDO.getPmWuLi());
            wuJiangDO.setPvTongShuai(wuJiangDO.getPbTongShuai() + (level + wuJiangDO.getPaTongShuai()) * wuJiangDO.getPmTongShuai());
            wuJiangDO.setPvZhengZhi(wuJiangDO.getPbZhengZhi() + (level + wuJiangDO.getPaZhengZhi()) * wuJiangDO.getPmZhengZhi());
            wuJiangDO.setPvZhiLi(wuJiangDO.getPbZhiLi() + (level + wuJiangDO.getPaZhiLi()) * wuJiangDO.getPmZhiLi());
            wuJiangDO.setPvSuDu(wuJiangDO.getPbSuDu() + (level + wuJiangDO.getPaSuDu()) * wuJiangDO.getPmSuDu());
            wuJiangDO.setPvMeiLi(wuJiangDO.getPbMeiLi() + (level + wuJiangDO.getPaMeiLi()) * wuJiangDO.getPmMeiLi());

            // 基于兵种适性调节属性
            wuJiangDO.setPvWuLi(wuJiangDO.getPvWuLi() * propertyRateByBingZhong);
            wuJiangDO.setPvTongShuai(wuJiangDO.getPvTongShuai() * propertyRateByBingZhong);
            wuJiangDO.setPvZhengZhi(wuJiangDO.getPvZhengZhi() * propertyRateByBingZhong);
            wuJiangDO.setPvZhiLi(wuJiangDO.getPvZhiLi() * propertyRateByBingZhong);
            wuJiangDO.setPvSuDu(wuJiangDO.getPvSuDu() * propertyRateByBingZhong);
            wuJiangDO.setPvMeiLi(wuJiangDO.getPvMeiLi() * propertyRateByBingZhong);

            logBingZhong(wuJiangDO.getUserId(), wuJiangDO.getName(), bingZhong, bingZhongShiXing, propertyRateByBingZhong);
        });

    }

    // 准备回合，基于部队士气，调节两只队伍的武将伤害倍率
    private static void prepareShiQi(PKBO PKBO) {
        formatStringPrint(" 部队士气加成\n", 33, 0);

        Group firstGroup = PKBO.getFirstGroup();
        Group secondGroup = PKBO.getSecondGroup();

        // 基于队伍士气，调整两支队伍的武将的伤害倍率
        prepareShiQi(firstGroup);
        prepareShiQi(secondGroup);
    }

    private static void prepareShiQi(Group group) {
        List<WuJiangDO> groupList = group.getFightGroup();
        Short groupShiQi = group.getShiQi();
        String bingZhong = group.getBingZhong();

        // 基于该队伍士气值，计算士气伤害倍率
        Float harmRateByShiQi;
        if (groupShiQi >= 100) {
            harmRateByShiQi = 1f;
        } else {
            harmRateByShiQi = (float) (0.007 * groupShiQi + 0.3);
        }

        // 修正该队伍所有武将的 士气伤害倍率
        groupList.forEach(wuJiangDO -> {
            //
            wuJiangDO.setHarmRateByShiQi(wuJiangDO.getHarmRateByShiQi() * harmRateByShiQi);
        });

        WuJiangDO wuJiangDo = groupList.getFirst();
        Integer userId = wuJiangDo.getUserId();
        String name = wuJiangDo.getName();

        String str1 = "   " + bingZhong + "  [" + userId.toString() + "+" + name + "]"
                + "队当前士气值为" + groupShiQi +
                "，造成伤害" +
                (groupShiQi >= 100 ? "不变" : "降低" + percentInstance.format(1 - harmRateByShiQi))
                + "\n";

        formatStringPrint(str1, 29, 0);
    }

    private static void init(Group firstGroup, Group secondGroup, Map<Integer, WuJiangDO> wuJiangMapById) {
        List<WuJiangDO> firstGroupAll = firstGroup.getAllGroup();
        List<WuJiangDO> secondGroupAll = secondGroup.getAllGroup();

        // 设置两只队伍的主将
        firstGroup.setMain(firstGroupAll.getFirst());
        secondGroup.setMain(secondGroupAll.getFirst());

        // 遍历group1
        firstGroupAll.forEach(wuJiangDO -> {
            // 基于数据库查询的数据，给该武将赋值
            BeanUtils.copyProperties(wuJiangMapById.get(wuJiangDO.getId()), wuJiangDO);

            // 给该武将赋予 队友队伍
            wuJiangDO.setTeammates(firstGroup);
            // 给该武将赋予 敌人队伍
            wuJiangDO.setEnemy(secondGroup);
        });

        secondGroupAll.forEach(wuJiangDO -> {
            BeanUtils.copyProperties(wuJiangMapById.get(wuJiangDO.getId()), wuJiangDO);

            wuJiangDO.setTeammates(secondGroup);
            wuJiangDO.setEnemy(firstGroup);
        });

        // 再拷贝一份给能参加战斗的 List
        firstGroup.getFightGroup().addAll(firstGroupAll);
        secondGroup.getFightGroup().addAll(secondGroupAll);
    }
}
